package process.module;

import java.util.List;
import java.util.Set;

import process.model.OntoResource;
import process.model.SimpleAnnotation;

/**
 * @author tvo35
 * 
 */

public class LevenshteinDistanceAnnotated {
	private Double normalizedDistanceThreshold = new Double(0.15);
	private double deleteCost = 1;
	private double insertCost = 1;
	private double substituteCost = 1;

	public LevenshteinDistanceAnnotated(double distanceThreshold) {
		this.normalizedDistanceThreshold = distanceThreshold;
	}

	public LevenshteinDistanceAnnotated() {
	}

	public void annotate(String input, List<SimpleAnnotation> annotations, Set<OntoResource> resourcesSet,
			String string, String defaultString, OntoResource resource ) {
		input = input + " "; // to avoid out of boundaries problems
		char[] content = input.toCharArray();
		double[] distancesPrevCol = new double[string.length() + 1];
		int[] insertionsPrevCol = new int[string.length() + 1];
		int[] deletionsPrevCol = new int[string.length() + 1];
		double[] distancesCol = new double[string.length() + 1];
		int[] insertionsCol = new int[string.length() + 1];
		int[] deletionsCol = new int[string.length() + 1];
		double distanceThreshold;

		if (string.length() != 0) {
			distanceThreshold = (normalizedDistanceThreshold * string.length());
		} else
			distanceThreshold = -1.0; // We do not annotate empty strings.

		for (int i = 0; i < string.length() + 1; i++) {
			distancesPrevCol[i] = i;
			deletionsPrevCol[i] = i;
		}

		double prevPrevDistance = Double.MAX_VALUE;
		for (int j = 0; j < content.length; j++) {
			for (int i = 1; i < distancesCol.length; i++) {
				if (string.charAt(i - 1) == content[j]) {
					distancesCol[i] = distancesPrevCol[i - 1];
					deletionsCol[i] = deletionsPrevCol[i - 1];
					insertionsCol[i] = insertionsPrevCol[i - 1];
				} else {
					double delete = distancesCol[i - 1] + deleteCost;
					double insert = distancesPrevCol[i] + insertCost;
					double substitute = distancesPrevCol[i - 1]
							+ substituteCost;
					if (delete <= insert && delete <= substitute) {
						distancesCol[i] = delete;
						deletionsCol[i] = deletionsCol[i - 1] + 1;
						insertionsCol[i] = insertionsCol[i - 1];
					} else if (insert <= substitute) {
						distancesCol[i] = insert;
						deletionsCol[i] = deletionsPrevCol[i];
						insertionsCol[i] = insertionsPrevCol[i] + 1;
					} else {
						distancesCol[i] = substitute;
						deletionsCol[i] = deletionsPrevCol[i - 1];
						insertionsCol[i] = insertionsPrevCol[i - 1];
					}
				}
			}
			double distance = distancesCol[string.length()];
			double prevDistance = distancesPrevCol[string.length()];
			if (distance >= prevDistance && prevDistance <= prevPrevDistance) {
				if (prevDistance <= distanceThreshold) {
					int matchedRegionEnd = j;
					int matchedRegionStart = (j)
							- (string.length()
									+ insertionsPrevCol[string.length()] - deletionsPrevCol[string
										.length()]);

					int startOffset = 0;
					int endOffset = 0;

					if (matchedRegionEnd + endOffset < content.length) {
						while (!Character.isWhitespace(content[matchedRegionEnd
								+ endOffset])) {
							endOffset++;
							if (matchedRegionEnd + endOffset == content.length - 1)
								break;
						}
					}

					if (matchedRegionStart + startOffset > 0) {
						while (!Character
								.isWhitespace(content[matchedRegionStart
										+ startOffset - 1])) {
							startOffset--;
							if (matchedRegionStart + startOffset == 0)
								break;
						}
					}

					double currentDistance = prevDistance
							+ (endOffset - startOffset) * insertCost;

					if (currentDistance <= distanceThreshold) {
						putAnnotation(annotations, defaultString, currentDistance,
								matchedRegionStart + startOffset,
								matchedRegionEnd + endOffset);
						resourcesSet.add(resource);
					}
				}
			}

			prevPrevDistance = distancesPrevCol[string.length()];
			for (int i = 1; i < string.length() + 1; i++) {
				distancesPrevCol[i] = distancesCol[i];
				insertionsPrevCol[i] = insertionsCol[i];
				deletionsPrevCol[i] = deletionsCol[i];

			}
		}
	}

	private void putAnnotation(List<SimpleAnnotation> annotations,
			String matchedString, double distance, int matchedRegionStart,
			int matchedRegionEnd) {
		annotations.add(new SimpleAnnotation(matchedRegionStart,
				matchedRegionEnd, matchedString, distance, distance
						/ matchedString.length()));
		System.out.println(matchedString + "  " + distance / matchedString.length() + "  "
				+ (matchedRegionStart) + "  " + (matchedRegionEnd));
	}
}
